/**
* Esta clase representa un juego de sudoku. Esta contiene la solucion,
* la entrada de usuario, el numero seleccioando, la dificultad del juego
* y metodos para chequear la validacion
* de la entrada de usuario.
*/
package sudoku.Modelo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Observable;
import sudoku.Extras.Tripla;
/**
* @author User
*/
public class Juego extends Observable {
public int[][] mISolucion; // Solucion Generada.
public int[][] mIJuego; // Juego Generado con entrada de usuario.
public Tripla[][] mIGuardado;
public boolean[][] mBComprobar; // Para Verificar validez del juego.
private int iNumSeleccionado; // Numero Seleccionado por Usuario.
private boolean bAyuda; // Ayuda si o no.
private int dificultad; // Dificultad elejida
private boolean fin;
/**
* Constructor
*/
public Juego() {
dificultad = 1;
mISolucion = new int[9][9];
mIJuego = new int[9][9];
mBComprobar = new boolean[9][9];
mIGuardado = new Tripla[9][9];
for (int y = 0; y < 9; y++) {
for (int x = 0; x < 9; x++){
mIGuardado[y][x] = new Tripla();
}
}
bAyuda = false;
}
/**
* Genera un nuevo sudoku
* Todos los observadores son notificados, Codigo de Accion = nuevo juego.
*/
public void nuevoJuego(int dificultad) {
this.dificultad = dificultad;
mISolucion = generarSolucion(new int[9][9], 0);
mIJuego = generarJuego(copiar(mISolucion),dificultad);
estadoInicial(mIGuardado);
setChanged();
notifyObservers("Nuevo");
}
/**
* Genera un estado inicial del sudoku.
* Almacenando en una matriz de 9x9 de triplas, las cuales en cada
* una contiene:
* primer componente el numero,
* segundo: 0 si fue juego inicial, 1 si es del usuario.
* tercero: la solucion de ese campo..
*/
private void estadoInicial(Tripla[][] mIEstado){
for (int y = 0; y < 9; y++) {
for (int x = 0; x < 9; x++){
mIEstado[y][x].setPri(mIJuego[y][x]);
mIEstado[y][x].setSeg(0);
mIEstado[y][x].setTer(mISolucion[y][x]);
}
}
}
public void cargarEstado(Tripla[][] mIEstado){
for (int y = 0; y < 9; y++) {
for (int x = 0; x < 9; x++){
mIJuego[y][x] = mIEstado[y][x].getPri();
mISolucion[y][x]= mIEstado[y][x].getTer();
}
}
mIGuardado = mIEstado;
setChanged();
notifyObservers("Cargar");
}
/**
* Comprueba la entrada de usuario con la solucion y la alamcena en una matriz de valicacion
* Todos los observadores son notificados, Codigo de Accion = Chequear.
*/
public boolean comprobarJuego() {
iNumSeleccionado = 0;
fin = true;
for (int y = 0; y < 9; y++) {
for (int x = 0; x < 9; x++){
mBComprobar[y][x] = mIJuego[y][x] == mISolucion[y][x];
fin = fin && (mBComprobar[y][x]);
}
}
if (fin){
return fin;
}else{
setChanged();
notifyObservers("Chequear");
return false;
}
}
public void finalizar(){
setChanged();
notifyObservers("Solucionar");
}
/**
* Setea la ayuda (activada o desactivada)
* Todos los observadores son notificados, Codigo de Accion = Ayuda.
*
* @param bAyuda True para ayuda activada, False caso contrario.
*/
public void setAyuda(boolean bAyuda) {
this.bAyuda = bAyuda;
setChanged();
notifyObservers("Ayuda");
}
/**
* Setea el Campo iNumSeleccionado por la entrada de usuario
* Todos los observadores son notificados, Codigo de Accion = Numero Seleccionado.
*
* @param selectedNumber Number selected by user.
*/
public void setNumSeleccionado(int iNumSeleccionado) {
this.iNumSeleccionado = iNumSeleccionado;
setChanged();
notifyObservers("NumeroSeleccionado");
}
/**
* Setea la dificultad del juego.
*
* @param dificultad dificultad elegida.
*/
public void setDificultad(int dificultad){
this.dificultad = dificultad;
setChanged();
notifyObservers("Dificultad");
}
/**
* Devuelve la dificultad del juego.
*
* @return dificultad dificultad del juego.
*/
public int getDificultad(){
return dificultad;
}
/**
* Retorna el numero seleccionado por el usuario.
*
* @return el Numero seleccionado por el usuario.
*/
public int getNumSeleccionado() {
return iNumSeleccionado;
}
/**
* Retrona True si la ayuda esta activada, False caso contrario.
*
* @return True Ayuda activada, False Caso Contrario.
*/
public boolean conAyuda() {
return bAyuda;
}
/**
* Retorna si el numero seleccionado es candidato o no en la pocision dada.
*
* @param x X posicion en el juego.
* @param y Y posicion en el juego.
* @return True si numSeleccionado es candidatoen la posicion dada, false caso contrario.
*/
public boolean esCandidatoNumSeleccionado(int x, int y) {
boolean esCandidato = (mIJuego[y][x] == 0 && esCandidato(mIJuego, y, x, iNumSeleccionado));
return esCandidato;
}
/**
* Setea un numero dado en una pocicion dada.
*
* @param x Posicion X en el juego.
* @param y Posicion Y en el Juego.
* @param numero El Numero a Cambiar.
*/
public void setNumero(int x, int y, int numero) {
mIJuego[y][x] = numero;
mIGuardado[y][x].setSeg(1);
mIGuardado[y][x].setPri(numero);
}
/**
* Retorna el numero de la posicion dada.
*
* @param x Posicion X del Juego.
* @param y Posicion Y del Juego.
* @return El Numero de la posicion.
*/
public int getNumero(int x, int y) {
int numero = mIJuego[y][x];
return numero;
}
public Tripla[][] getJuegoTemporal() {
return mIGuardado;
}
/**
* Devuelve si la entrada de usuario es valida en la posicion dada .
*
* @param x Posicion X del Juego.
* @param y Posicion Y del Juego.
* @return True si la entrada de usaurio en la posicion dada es valida,
* False Caso Contrario.
*/
public boolean esValida(int x, int y) {
return mBComprobar[y][x];
}
/**
* Retorna si el numero dado es candidato en la posicion dada.
* @param juego Juego para comprobar.
* @param y Posicion y del juego.
* @param x Posicion x del juego.
* @param numero Numero a chequear.
* @return True si el numero es candidato, false caso contrario.
*/
private boolean esCandidato(int[][] juego, int y, int x, int numero) {
// Verifica por fila Y si es valido
for (int c = 0; c < 9; c++) {
if (juego[y][c] == numero)
return false;
}
// Verifica por columna X si es valido.
for (int f = 0; f < 9; f++) {
if (juego[f][x] == numero)
return false;
}
// Verifica por el bloque, al que pertenece si es valido
int x1 = (x/3)*3;
int y1 = (y/3)*3;
for (int ff = y1; ff < y1+3; ff++) {
for (int cc = x1; cc < x1+3; cc++) {
if (juego[ff][cc] == numero)
return false;
}
}
return true;
}
/**
* Devuelve un numero posible (desde una lista) dada la posicion
* o -1 si no hay numero posible
*
* @param juego Juego a chequear.
* @param x X posicion en el juego.
* @param y Y posicion en el juego.
* @param numeros Lista de numeros.
* @return Un numero valido para la posicion dada, -1 si no se consigue numero.
*/
private int obtenerNumSigPosible(int[][] juego, int y, int x, List<Integer> numeros) {
while (numeros.size() > 0) {
int numero = numeros.remove(0);
if (esCandidato(juego, y, x, numero))
return numero;
}
return -1;
}
/**
* Genera Solucion del jeugo.
*
* @param Juego Juego a crear, se le pasa una nueva matriz 'int[9][9]' la primera vez.
* @param indice Indice corriente, 0 la primera vez.
* @return Solucion de Juego Sudoku .
*/
private int[][] generarSolucion(int[][] juego, int indice) {
if (indice > 80)
return juego;
int x = indice % 9;
int y = indice / 9;
List<Integer> numeros = new ArrayList<>();
//Lista Con Numeros del 1 al 9.
for (int i = 1; i <= 9; i++)
numeros.add(i);
Collections.shuffle(numeros);
while (numeros.size() > 0) {
int numero = obtenerNumSigPosible(juego, y, x, numeros);
if (numero == -1)
return null;
juego[y][x] = numero;
int[][] tmpJuego = generarSolucion(juego, indice + 1);
if (tmpJuego != null)
return tmpJuego;
juego[y][x] = 0;
}
return null;
}
/**
* Genera Juego Sudoku a partir de una solucion.
*
* @param juego Juego a ser generado.
* @return Juego del sudoku generado.
*/
private int[][] generarJuego(int[][] juego, int dificultad) {
List<Integer> posiciones = new ArrayList<>();
for (int i = 0; i < 81; i++)
posiciones.add(i);
Collections.shuffle(posiciones);
int cantidadNoCeros = 0;
// Seteamos la cantidad segun la dificultad.
if (dificultad == 1){
cantidadNoCeros = 60 ;
} else if (dificultad == 2){
cantidadNoCeros = 45 ;
}else if (dificultad == 3){
cantidadNoCeros = 30 ;
}
while (posiciones.size() > cantidadNoCeros) {
int posicion = posiciones.remove(0);
int x = posicion % 9;
int y = posicion / 9;
int temp = juego[y][x];
juego[y][x] = 0;
if (!esValido(juego))
juego[y][x] = temp;
}
return juego;
}
/**
* Comprueba si el juego dado es valido.
*
* @param juego Juego a comprobar.
* @return True si juego es valido, false caso contrario.
*/
private boolean esValido(int[][] juego) {
return esValido(juego, 0, new int[] {0} );
}
/**
* Comprueba si el juego dado es valido, se llama recursivamente.
* Deberia dar true si es solucion unica.
*
* @param juego Juego a comprobar.
* @param indice Indice corriente a comprobar.
* @param numeroDeSoluciones Numero de soluciones encontradas.
* @return True si juego es valido, falso caso contrario.
*/
private boolean esValido(int[][] juego, int indice, int[] numeroDeSoluciones) {
if (indice > 80)
return ++numeroDeSoluciones[0] == 1;
int x = indice % 9;
int y = indice / 9;
if (juego[y][x] == 0) {
List<Integer> numeros = new ArrayList<>();
for (int i = 1; i <= 9; i++)
numeros.add(i);
while (numeros.size() > 0) {
int numero = obtenerNumSigPosible(juego, y, x, numeros);
if (numero == -1)
break;
juego[y][x] = numero;
if (!esValido(juego, indice + 1, numeroDeSoluciones)) {
juego[y][x] = 0;
return false;
}
juego[y][x] = 0;
} //end while
}
else if (!esValido(juego, indice + 1, numeroDeSoluciones))
return false;
return true;
}
/**
* Copia el Juego.
*
* @param juego Juego a copiar.
* @return Copia del juego.
*/
private int[][] copiar(int[][] juego) {
int[][] copia = new int[9][9];
for (int y = 0; y < 9; y++) {
System.arraycopy(juego[y], 0, copia[y], 0, 9);
}
return copia;
}
/*
* Representacion texto de la matriz.
*
* @param juego juego a ser representado.
*/
public void toString(int[][] juego) {
System.out.println();
for (int y = 0; y < 9; y++) {
for (int x = 0; x < 9; x++) {
System.out.print(" " + juego[y][x]);
}
System.out.println();
}
}
}